package com.sz.common.core.service.thread;

import com.sz.common.base.logger.Logger;

import com.sz.common.base.utils.JsonUtils;
import com.sz.common.core.service.queue.Queue;
import com.sz.common.core.service.queue.Queues;
import com.sz.common.core.system.entity.OperationLog;
import com.sz.common.core.system.service.OperationLoggerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import java.util.HashMap;
import java.util.Map;

/**
 * Function: TODO: ADD FUNCTION <br>
 * Author: Charles. <br>
 * Date: 2017-05-23 11:20:00.
 */
@Component
public class PrincipalLogsConsumer implements RunnableBean {
    protected Logger logger = Logger.getLogger(this.getClass());
    private Queue<OperationLog> queue = null;
    private Map<Integer, Integer> failedCounter = new HashMap<>();
    private boolean isRunning = false;

    @Autowired
    OperationLoggerService operationLoggerService;

    public PrincipalLogsConsumer() {
        queue = Queues.getPrincipalLogsQueue();
    }

    @Override
    public String getName() {
        return "PrincipalLogsConsumer";
    }

    @PreDestroy
    @Override
    public void stopSignal() {
        if (isRunning) {
            OperationLog stopSignal = new OperationLog();
            stopSignal.setId(Long.MIN_VALUE);
            queue.add(stopSignal);
        }
    }

    @Override
    public void run() {
        String msg = String.format("Enter thread %s, thread id %s", this.getName(), Thread.currentThread().getId());
        logger.debug(msg);
        isRunning = true;
        while (true) {
            OperationLog log = null;
            try {
                log = queue.take(); //这里会线程阻塞
                if (log.getId() != null && Long.MIN_VALUE == log.getId()) { //停止信号
                    logger.debug("prepare to exit current thread:" + this.getName());
                    break;
                }
                operationLoggerService.addOperationLog(log); //write log to db
                removeFailedElement(log.hashCode()); // ensure success
            } catch (Throwable t) {
                logger.error("error occurred when writing operation log to db, try it later", t);
                if (log != null) {
                    int count = addFailedElement(log.hashCode()); //添加错误计数
                    if (count <= 10) {
                        queue.add(log);  //add back to queue
                    } else {
                        removeFailedElement(log.hashCode()); // discard current log， write to log file
                        logger.error("tried 10 times, error continuing, write to logs:" + JsonUtils.toJSONString(log));
                    }
                }
            }
        }
        isRunning = false;
    }

    private int addFailedElement(int hashCode) {
        int count = 0;
        if (this.failedCounter.containsKey(hashCode)){
            count = this.failedCounter.get(hashCode);
        }
        count = count + 1;
        this.failedCounter.put(hashCode, count);
        return count;
    }

    private void removeFailedElement(int hashCode) {
        this.failedCounter.remove(hashCode);
    }

}
